<template>
  <view class="u-code-input">
    <view
      class="u-code-input__item"
      :style="[itemStyle(index)]"
      v-for="(item, index) in codeLength"
      :key="index"
    >
      <view
        class="u-code-input__item__dot"
        v-if="dot && codeArray.length > index"
      ></view>
      <text
        v-else
        :style="{
					fontSize: addUnit(fontSize),
					fontWeight: bold ? 'bold' : 'normal',
					color: color
				}"
      >{{codeArray[index]}}</text>
      <view
        class="u-code-input__item__line"
        v-if="mode === 'line'"
        :style="[lineStyle]"
      ></view>
      <!-- #ifndef APP-PLUS -->
      <view v-if="isFocus && codeArray.length === index" :style="{backgroundColor: color}" class="u-code-input__item__cursor"></view>
      <!-- #endif -->
    </view>
    <input
      :disabled="disabledKeyboard"
      type="text"
      :focus="focus"
      :value="inputValue"
      :maxlength="maxlength"
      :adjustPosition="adjustPosition"
      class="u-code-input__input"
      @input="inputHandler"
      :style="{
				height: addUnit(size)
			}"
      @focus="isFocus = true"
      @blur="isFocus = false"
    />
  </view>
</template>

<script>
  import props from './props';
  /**
   * CodeInput 验证码输入
   * @description 该组件一般用于验证用户短信验证码的场景，也可以结合uview-plus的键盘组件使用
   * @tutorial https://ijry.github.io/uview-plus/components/codeInput.html
   * @property {String | Number}	maxlength			最大输入长度 （默认 6 ）
   * @property {Boolean}			dot					是否用圆点填充 （默认 false ）
   * @property {String}			mode				显示模式，box-盒子模式，line-底部横线模式 （默认 'box' ）
   * @property {Boolean}			hairline			是否细边框 （默认 false ）
   * @property {String | Number}	space				字符间的距离 （默认 10 ）
   * @property {String | Number}	value				预置值
   * @property {Boolean}			focus				是否自动获取焦点 （默认 false ）
   * @property {Boolean}			bold				字体和输入横线是否加粗 （默认 false ）
   * @property {String}			color				字体颜色 （默认 '#606266' ）
   * @property {String | Number}	fontSize			字体大小，单位px （默认 18 ）
   * @property {String | Number}	size				输入框的大小，宽等于高 （默认 35 ）
   * @property {Boolean}			disabledKeyboard	是否隐藏原生键盘，如果想用自定义键盘的话，需设置此参数为true （默认 false ）
   * @property {String}			borderColor			边框和线条颜色 （默认 '#c9cacc' ）
   * @property {Boolean}			disabledDot			是否禁止输入"."符号 （默认 true ）
   *
   * @event {Function}	change	输入内容发生改变时触发，具体见上方说明			value：当前输入的值
   * @event {Function}	finish	输入字符个数达maxlength值时触发，见上方说明	value：当前输入的值
   * @example	<u-code-input v-model="value4" :focus="true"></u-code-input>
   */
  export default {
    mixins: [props],
    data() {
      return {
        inputValue: '',
        isFocus: this.focus
      }
    },
    watch: {
      // #ifdef VUE2
      value: {
        // #endif
        // #ifdef VUE3
        modelValue: {
          // #endif
          immediate: true,
          handler(val) {
            // 转为字符串，超出部分截掉
            this.inputValue = String(val).substring(0, this.maxlength)
          }
        },
      },
      computed: {
        // 根据长度，循环输入框的个数，因为头条小程序数值不能用于v-for
        codeLength() {
          return new Array(Number(this.maxlength))
        },
        // 循环item的样式
        itemStyle() {
          return index => {
            const style = {
              width: this.addUnit(this.size),
              height: this.addUnit(this.size)
            }
            // 盒子模式下，需要额外进行处理
            if (this.mode === 'box') {
              // 设置盒子的边框，如果是细边框，则设置为0.5px宽度
              style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}`
              // 如果盒子间距为0的话
              if (this.getPx(this.space) === 0) {
                // 给第一和最后一个盒子设置圆角
                if (index === 0) {
                  style.borderTopLeftRadius = '3px'
                  style.borderBottomLeftRadius = '3px'
                }
                if (index === this.codeLength.length - 1) {
                  style.borderTopRightRadius = '3px'
                  style.borderBottomRightRadius = '3px'
                }
                // 最后一个盒子的右边框需要保留
                if (index !== this.codeLength.length - 1) {
                  style.borderRight = 'none'
                }
              }
            }
            if (index !== this.codeLength.length - 1) {
              // 设置验证码字符之间的距离，通过margin-right设置，最后一个字符，无需右边框
              style.marginRight = this.addUnit(this.space)
            } else {
              // 最后一个盒子的有边框需要保留
              style.marginRight = 0
            }

            return style
          }
        },
        // 将输入的值，转为数组，给item历遍时，根据当前的索引显示数组的元素
        codeArray() {
          return String(this.inputValue).split('')
        },
        // 下划线模式下，横线的样式
        lineStyle() {
          const style = {}
          style.height = this.hairline ? '2px' : '4px'
          style.width = this.addUnit(this.size)
          // 线条模式下，背景色即为边框颜色
          style.backgroundColor = this.borderColor
          return style
        }
      },
      emits: ["change", 'finish', "update:modelValue"],
      methods: {
        getPx(value, unit = false) {
          if (/^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)) {
            return unit ? `${value}px` : Number(value)
          }
          // 如果带有rpx，先取出其数值部分，再转为px值
          if (/(rpx|upx)$/.test(value)) {
            return unit ? `${uni.rpx2px(parseInt(value))}px` : Number(uni.rpx2px(parseInt(value)))
          }
          return unit ? `${parseInt(value)}px` : parseInt(value)
        },
        addUnit(value = 'auto', unit = '') {
          if (!unit) {
            unit = 'px'
          }
          value = String(value)
          // 用uView内置验证规则中的number判断是否为数值
          return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value) ? `${value}${unit}` : value
        },
        // 监听输入框的值发生变化
        inputHandler(e) {
          const value = e.detail.value
          this.inputValue = value
          // 是否允许输入“.”符号
          if(this.disabledDot) {
            this.$nextTick(() => {
              this.inputValue = value.replace('.', '')
            })
          }
          // 未达到maxlength之前，发送change事件，达到后发送finish事件
          this.$emit('change', value)
          // 修改通过v-model双向绑定的值
          // #ifdef VUE3
          this.$emit("update:modelValue", value);
          // #endif
          // #ifdef VUE2
          this.$emit("input", value);
          // #endif
          // 达到用户指定输入长度时，发出完成事件
          if (String(value).length >= Number(this.maxlength)) {
            this.$emit('finish', value)
          }
        }
      }
    }
</script>

<style lang="scss" scoped>
  @mixin flex($direction: row) {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    flex-direction: $direction;
  }
  $u-code-input-cursor-width: 1px;
  $u-code-input-cursor-height: 40%;
  $u-code-input-cursor-animation-duration: 1s;
  $u-code-input-cursor-animation-name: u-cursor-flicker;

  .u-code-input {
    @include flex;
    position: relative;
    overflow: hidden;

    &__item {
      @include flex;
      justify-content: center;
      align-items: center;
      position: relative;

      &__text {
        font-size: 15px;
        color: $u-content-color;
      }

      &__dot {
        width: 7px;
        height: 7px;
        border-radius: 100px;
        background-color: $u-content-color;
      }

      &__line {
        position: absolute;
        bottom: 0;
        height: 4px;
        border-radius: 100px;
        width: 40px;
        background-color: $u-content-color;
      }
      /* #ifndef APP-PLUS */
      &__cursor {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-50%);
        width: $u-code-input-cursor-width;
        height: $u-code-input-cursor-height;
        animation: $u-code-input-cursor-animation-duration u-cursor-flicker infinite;
      }
      /* #endif */

    }

    &__input {
      // 之所以需要input输入框，是因为有它才能唤起键盘
      // 这里将它设置为两倍的屏幕宽度，再将左边的一半移出屏幕，为了不让用户看到输入的内容
      position: absolute;
      left: -750rpx;
      width: 1500rpx;
      top: 0;
      background-color: transparent;
      text-align: left;
    }
  }

  /* #ifndef APP-PLUS */
  @keyframes u-cursor-flicker {
    0% {
      opacity: 0;
    }
    50% {
      opacity: 1;
    }
    100% {
      opacity: 0;
    }
  }
  /* #endif */

</style>
